// copyright 1993 Michael B. Johnson; some portions copyright 1994, MIT
// see COPYRIGHT for reuse legalities
//

#import "RIBNuPatch.h"

@implementation RIBNuPatch

+ initialize { return [RIBNuPatch setVersion:1], self; }

- (BOOL)hasBoundingBox { return YES; }

- init
{
  [super init];

  nU = nV = uOrder = vOrder = 0;
  uKnot = vKnot = NULL;
  uMin  = uMax = vMin = vMax = 0.0;
  nVPlusVOrder = (char *)NXZoneMalloc([self zone], 128);
  nUPlusUOrder = (char *)NXZoneMalloc([self zone], 128);
  tesselationVector[0] = 6.0;
  tesselationVector[1] = 6.0;

  return self;
}

- awake
{
  [super awake];

  return self;
}

- free
{
  //if (uKnot) { free(uKnot); }
  //if (vKnot) { free(vKnot); }
  //free(nVPlusVOrder);
  //free(nUPlusUOrder);

  return [super free];
}

- copyFromZone:(NXZone *)zone
{
   id   newCopy = [super copyFromZone:zone];

   NXLogError("WARNING: copyFromZone: incompletely implemented for %s : I'm leaking memory in -free", 
	      [[self class] name]);
   return newCopy;
}

- setNU:(RtInt)newNU uOrder:(RtInt)newUOrder uKnot:(RtFloat *)newUKnot uMin:(RtFloat)newUMin uMax:(RtFloat)newUMax 
     nV:(RtInt)newNV vOrder:(RtInt)newVOrder vKnot:(RtFloat *)newVKnot vMin:(RtFloat)newVMin vMax:(RtFloat)newVMax
     n:(int)newN tokens:(RtToken *)newTokens parms:(RtPointer *)newParms archiveVector:(char **)newArchiveVector
     printfTypeVector:(int *)newPrintfTypeVector printfNVector:(int *)newPrintfNVector
{  
   nU = newNU;
   nV = newNV;
   uOrder = newUOrder;
   vOrder = newVOrder;
   uKnot = newUKnot; 
   vKnot = newVKnot; 
   uMin = newUMin;
   uMax = newUMax;
   vMin = newVMin;
   vMax = newVMax;

   // 
   if (uOrder > 4)
   {  tesselationVector[0] = 1.0;
   }
   if (uOrder == 4)
   {  tesselationVector[0] = 2.0;
   }
   if (uOrder == 3)
   {  tesselationVector[0] = 4.0;
   }
   if (uOrder == 2)
   {  tesselationVector[0] = 6.0;
   }

   // 
   if (vOrder > 4)
   {  tesselationVector[1] = 1.0;
   }
   if (vOrder == 4)
   {  tesselationVector[1] = 2.0;
   }
   if (vOrder == 3)
   {  tesselationVector[1] = 4.0;
   }
   if (vOrder == 2)
   {  tesselationVector[1] = 6.0;
   }


   [self setN:newN tokens:newTokens parms:newParms archiveVector:newArchiveVector printfTypeVector:newPrintfTypeVector printfNVector:newPrintfNVector];

   dirtyBoundingBox = TRUE;
   return self;
}

- (RtInt)nU {  return nU; }
- (RtInt)uOrder {  return uOrder; }
- (RtFloat *)uKnot {  return uKnot; }
- (RtFloat)uMin {  return uMin; }
- (RtFloat)uMax {  return uMax; }
- (RtInt)nV {  return nV; }
- (RtInt)vOrder {  return vOrder; }
- (RtFloat *)vKnot {  return vKnot; }
- (RtFloat)vMin {  return vMin; }
- (RtFloat)vMax {  return vMax; }


- (BOOL)theSameAs:otherRIBCommand
{
  int      i;
  RtFloat  *otherUKnot, *otherVKnot;


  if ([self class] != [otherRIBCommand class])
  {  return NO;
  }
  if (nU != [otherRIBCommand nU])
  {  return NO;
  }
  if (nV != [otherRIBCommand nV])
  {  return NO;
  }
  if (uOrder != [otherRIBCommand uOrder])
  {  return NO;
  }
  if (vOrder != [otherRIBCommand vOrder])
  {  return NO;
  }
  if (uMin != [otherRIBCommand uMin])
  {  return NO;
  }
  if (uMax != [otherRIBCommand uMax])
  {  return NO;
  }
  if (vMin != [otherRIBCommand vMin])
  {  return NO;
  }
  if (vMax != [otherRIBCommand vMax])
  {  return NO;
  }

  otherUKnot = [otherRIBCommand uKnot];
  for (i = 0; i < (nU + uOrder); i++) 
  {  if (uKnot[i] != otherUKnot[i])
     {  return NO;
     }
  }

  otherVKnot = [otherRIBCommand vKnot];
  for (i = 0; i < (nV + vOrder); i++) 
  {  if (vKnot[i] != otherVKnot[i])
     {  return NO;
     }
  }

  return [super theSameAs:otherRIBCommand];
}


- (BOOL)similarTo:otherRIBCommand 
{
  int      i;
  RtFloat  *otherUKnot, *otherVKnot;


  if ([self class] != [otherRIBCommand class])
  {  return NO;
  }
  if (nU != [otherRIBCommand nU])
  {  return NO;
  }
  if (nV != [otherRIBCommand nV])
  {  return NO;
  }
  if (uOrder != [otherRIBCommand uOrder])
  {  return NO;
  }
  if (vOrder != [otherRIBCommand vOrder])
  {  return NO;
  }
  if (uMin != [otherRIBCommand uMin])
  {  return NO;
  }
  if (uMax != [otherRIBCommand uMax])
  {  return NO;
  }
  if (vMin != [otherRIBCommand vMin])
  {  return NO;
  }
  if (vMax != [otherRIBCommand vMax])
  {  return NO;
  }

  otherUKnot = [otherRIBCommand uKnot];
  for (i = 0; i < (nU + uOrder); i++) 
  {  if (uKnot[i] != otherUKnot[i])
     {  return NO;
     }
  }

  otherVKnot = [otherRIBCommand vKnot];
  for (i = 0; i < (nV + vOrder); i++) 
  {  if (vKnot[i] != otherVKnot[i])
     {  return NO;
     }
  }
  return YES;
}


- renderSelf:(WW3DCamera *)camera startingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
{
  // due to a bug in QuickRenderMan, the default tesselation values are way too high vis.a.vis. NURBS
  // Therefore, I wrap a call to RiGeometricApproximation here to speed things up...
  RiAttributeBegin();
    RiGeometricApproximation(RI_TESSELATION, RI_PARAMETRIC, tesselationVector, RI_NULL);
    RiNuPatchV(nU, uOrder, uKnot, uMin, uMax, nV, vOrder, vKnot, vMin, vMax, n, tokens, parms);
  RiAttributeEnd();
  return self;
}

- (BOOL)isMotionBlurrable { return YES; }

- writeEve:(NXStream *)stream atTabLevel:(int)tab
{
   int  i;


   for (i = 0; i < tab; i++)
   {  NXPrintf(stream, "\t");
   }
   NXPrintf(stream, "NuPatch %d %d {", nU, uOrder);
   for (i = 0; i < (nU + uOrder); i++) 
   {  NXPrintf(stream, "%f ", *(uKnot + i));
   }
   NXPrintf(stream, "} %f %f ",  uMin, uMax);
   NXPrintf(stream, "%d %d {", nV, vOrder);
   for (i = 0; i < (nV + vOrder); i++) 
   {  NXPrintf(stream, "%f ", *(vKnot + i));
   }
   NXPrintf(stream, "} %f %f ",  vMin, vMax);

   [super writeParameterList:stream];
   return self;
}

- writeInventorAtTime:(float)currentTime to:(NXStream *)stream atTabLevel:(int)tab
{
   int  i, k;


   for (i = 0; i < tab; i++)
   {  NXPrintf(stream, "\t");
   }
   NXPrintf(stream, "Separator {\n");

   // need to print out my points and other stuff here...
   [self writeParameterListForInventor:stream atTabLevel:(tab+1)];

   // if ((uMin || vMin) != 0.0) || ((uMax || vMax) != 1.0), I need to dump out st values here... 

   for (k = 0; k < (tab+1); k++) {  NXPrintf(stream, "\t"); }
   NXPrintf(stream, "NurbsSurface {\n");
   for (k = 0; k < (tab+2); k++) {  NXPrintf(stream, "\t"); }
   NXPrintf(stream, "numUControlPoints %d\n", nU);
   for (k = 0; k < (tab+2); k++) {  NXPrintf(stream, "\t"); }
   NXPrintf(stream, "numVControlPoints %d\n", nV);

   for (k = 0; k < (tab+2); k++) {  NXPrintf(stream, "\t"); }
   NXPrintf(stream, "uKnotVector [");
   for (i=0; i<(nU+uOrder-1); i++)
   {	NXPrintf(stream, "%f, ", uKnot[i]);
   }
   NXPrintf(stream, "%f]\n", uKnot[i]);

   for (k = 0; k < (tab+2); k++) {  NXPrintf(stream, "\t"); }
   NXPrintf(stream, "vKnotVector [");
   for (i=0; i<(nV+vOrder-1);  i++)
   {	NXPrintf(stream, "%f, ", vKnot[i]);
   }
   NXPrintf(stream, "%f]\n", vKnot[i]);
   for (k = 0; k < (tab+1); k++) {  NXPrintf(stream, "\t"); }
   NXPrintf(stream, "}\n");
   for (k = 0; k < tab; k++) {  NXPrintf(stream, "\t"); }
   NXPrintf(stream, "}\n");

   return self;
}


#define typeVector "iiiiffff"
#define typeValues &nU, &nV, &uOrder, &vOrder, &uMin, &uMax, &vMin, &vMax

- read:(NXTypedStream*)stream 
{
    int version;


    [super read:stream];

    nVPlusVOrder = (char *)malloc(128);
    nUPlusUOrder = (char *)malloc(128);

NX_DURING
    version = NXTypedStreamClassVersion(stream,"RIBNuPatch");
    if (version == 0) NXReadTypes(stream,"i",&version), version=1;
    if (version == 1) {
	NXReadTypes(stream, typeVector, typeValues);
	uKnot = (RtFloat *)NXZoneMalloc([self zone], (int)(nU + uOrder)*sizeof(int));
	vKnot = (RtFloat *)NXZoneMalloc([self zone], (int)(nV + vOrder)*sizeof(int));
	NXReadArray(stream, "f", (int)(nU + uOrder), uKnot);
	NXReadArray(stream, "f", (int)(nV + vOrder), vKnot);
	NXReadArray(stream, "f", 2, tesselationVector);
    } 
NX_HANDLER
    NXLogError("in read: %s, exception [%d] raised.\n", 
               [[self class] name], NXLocalHandler.code);
   return nil;
NX_ENDHANDLER
    return self;
}

- write:(NXTypedStream*)stream 
{
    [super write:stream];
    NXWriteTypes(stream, typeVector, typeValues);
    NXWriteArray(stream, "f", (int)(nU + uOrder), uKnot);
    NXWriteArray(stream, "f", (int)(nV + vOrder), vKnot);
    NXWriteArray(stream, "f", 2, tesselationVector);
    return self;
}

@end
